package com.rin.executor;

import com.rin.annotation.FillAuthor;
import com.rin.annotation.FillId;
import com.rin.annotation.FillTime;
import com.rin.constant.FillType;
import com.rin.service.BaseService;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * @author RinMi
 * @since 2023/6/14
 */
public class AnnotationExecutor {

    @Resource
    private BaseService baseService;

    @Resource
    private FillIdRule fillIdRule;

    private static final Class<FillId> ID_CLASS = FillId.class;

    private static final Class<FillTime> TIME_CLASS = FillTime.class;

    private static final Class<FillAuthor> AUTHOR_CLASS = FillAuthor.class;

    private static final Class<FillIdRule> ID_RULE_CLASS = FillIdRule.class;

    public String insertExecutor(Object entity) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        //填充id
        Field idField = idFinder(entity.getClass());
        assert idField != null;
        String entityId = fillId(entity, idField);
        assert entityId != null;
        //填充时间
        fillTime(entity, FillType.INSERT);
        //填充名称
        fillAuthor(entity, FillType.INSERT);
        return entityId;
    }

    public void updateExecutor(Object entity){
        //填充时间
        fillTime(entity, FillType.UPDATE);
        //填充名称
        fillAuthor(entity, FillType.UPDATE);
    }

    private Field idFinder(Class<?> clazz) {
        Field aimField = null;
        Field[] fields = clazz.getDeclaredFields();
        //查找主键属性
        for (Field field : fields) {
            if (field.isAnnotationPresent(ID_CLASS)) {
                if (null == aimField) {
                    aimField = field;
                } else {
                    throw new RuntimeException("存在多个目标");
                }
            }
        }
        return aimField;
    }

    private String fillId(Object entity, Field field) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        String lastId = baseService.getLastId(entity);
        FillId annotation = field.getAnnotation(ID_CLASS);
        String value = annotation.value();
        Method method = ID_RULE_CLASS.getDeclaredMethod(value, String.class);
        lastId = String.valueOf(method.invoke(fillIdRule, lastId));
        Class<?> typeClass = field.getType();
        if (String.class.isAssignableFrom(typeClass)) {
            ReflectionUtils.makeAccessible(field);
            ReflectionUtils.setField(field, entity, lastId);
        } else {
            method = typeClass.getMethod("valueOf", String.class);
            Object typeObject = method.invoke(null, lastId);
            ReflectionUtils.makeAccessible(field);
            ReflectionUtils.setField(field, entity, typeObject);
        }
        return lastId;
    }

    private void fillTime(Object entity, String fillType) {
        LocalDateTime localDateTime = LocalDateTime.now();
        FillTime annotation;
        Field[] fields = entity.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(TIME_CLASS)) {
                annotation = field.getAnnotation(TIME_CLASS);
                if (Arrays.stream(annotation.value()).allMatch(fillType::equals)){
                    ReflectionUtils.makeAccessible(field);
                    ReflectionUtils.setField(field, entity, localDateTime);
                }
            }
        }
    }

    private void fillAuthor(Object entity, String fillType) {
        //TODO 获取用户名称
        String author = "admin";
        FillAuthor annotation;
        Field[] fields = entity.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(AUTHOR_CLASS)) {
                annotation = field.getAnnotation(AUTHOR_CLASS);
                if (Arrays.stream(annotation.value()).allMatch(fillType::equals)){
                    ReflectionUtils.makeAccessible(field);
                    ReflectionUtils.setField(field, entity, author);
                }
            }
        }
    }
}
